#include "config.h"
#include "pipes/pipes.h"
#include <string.h>
#include <stdio.h>

sObject* uobject_new_from_gc(int size, sObject* parent, char* name, BOOL user_object)
{
   sObject* self = gc_get_free_object(T_UOBJECT, user_object);
   
   SHASH(self).mTableSize = size;
   SHASH(self).mTable = (hash_it**)MALLOC(sizeof(hash_it*) * size);
   memset(SHASH(self).mTable, 0, sizeof(hash_it*)*size);

   SHASH(self).mEntryIt = NULL;

   SHASH(self).mCounter = 0;

   if(parent) SUOBJECT(self).mParent = parent; else SUOBJECT(self).mParent = self;

   SUOBJECT(self).mName = STRDUP(name);

   return self;
}

sObject* uobject_new_from_stack(int size, sObject* parent, char* name)
{
   sObject* self = stack_get_free_object(T_UOBJECT);
   
   SHASH(self).mTableSize = size;
   SHASH(self).mTable = (hash_it**)MALLOC(sizeof(hash_it*) * size);
   memset(SHASH(self).mTable, 0, sizeof(hash_it*)*size);

   SHASH(self).mEntryIt = NULL;

   SHASH(self).mCounter = 0;

   if(parent) SUOBJECT(self).mParent = parent; else SUOBJECT(self).mParent = self;

   SUOBJECT(self).mName = STRDUP(name);

   return self;
}

void uobject_root_init(sObject* self)
{
    hash_put(self, "unset", NFUN_NEW_GC(cmd_unset, NULL, TRUE));
    sObject* readline = UOBJECT_NEW_GC(8, self, "rl", TRUE);
    uobject_init(readline);
    hash_put(self, "rl", readline);
    hash_put(readline, "insert_text", NFUN_NEW_GC(cmd_readline_insert_text, NULL, TRUE));
    hash_put(readline, "delete_text", NFUN_NEW_GC(cmd_readline_delete_text, NULL, TRUE));
    hash_put(readline, "clear_screen", NFUN_NEW_GC(cmd_readline_clear_screen, NULL, TRUE));
    hash_put(readline, "point_move", NFUN_NEW_GC(cmd_readline_point_move, NULL, TRUE));
    hash_put(self, "completion", NFUN_NEW_GC(cmd_completion, NULL, TRUE));
    hash_put(self, "p", NFUN_NEW_GC(cmd_p, NULL, TRUE));
    hash_put(self, "jobs", NFUN_NEW_GC(cmd_jobs, NULL, TRUE));
    hash_put(self, "gcinfo", NFUN_NEW_GC(cmd_gcinfo, NULL, TRUE));
    hash_put(self, "stackinfo", NFUN_NEW_GC(cmd_stackinfo, NULL, TRUE));
    hash_put(self, "fg", NFUN_NEW_GC(cmd_fg, NULL, TRUE));
    hash_put(self, "exit", NFUN_NEW_GC(cmd_exit, NULL, TRUE));
    hash_put(self, "fselector", NFUN_NEW_GC(cmd_fselector, NULL, TRUE));
    hash_put(self, "sweep", NFUN_NEW_GC(cmd_sweep, NULL, TRUE));
    hash_put(self, "subshell", NFUN_NEW_GC(cmd_subshell, NULL, TRUE));
    hash_put(self, "print", NFUN_NEW_GC(cmd_print, NULL, TRUE));
    hash_put(self, "if", NFUN_NEW_GC(cmd_if, NULL, TRUE));
    hash_put(self, "break", NFUN_NEW_GC(cmd_break, NULL, TRUE));
    hash_put(self, "while", NFUN_NEW_GC(cmd_while, NULL, TRUE));
    hash_put(self, "return", NFUN_NEW_GC(cmd_return, NULL, TRUE));
    hash_put(self, "rehash", NFUN_NEW_GC(cmd_rehash, NULL, TRUE));
    hash_put(self, "true", NFUN_NEW_GC(cmd_true, NULL, TRUE));
    hash_put(self, "false", NFUN_NEW_GC(cmd_false, NULL, TRUE));
    hash_put(self, "write", NFUN_NEW_GC(cmd_write, NULL, TRUE));
    hash_put(self, "quote", NFUN_NEW_GC(cmd_quote, NULL, TRUE));
    hash_put(self, "load", NFUN_NEW_GC(cmd_load, NULL, TRUE));
    hash_put(self, "inherit", NFUN_NEW_GC(cmd_inherit, NULL, TRUE));
    hash_put(self, "eval", NFUN_NEW_GC(cmd_eval, NULL, TRUE));
    hash_put(self, "object", NFUN_NEW_GC(cmd_object, NULL, TRUE));
    hash_put(self, "pwo", NFUN_NEW_GC(cmd_pwo, NULL, TRUE));
    hash_put(self, "co", NFUN_NEW_GC(cmd_co, NULL, TRUE));
    hash_put(self, "ref", NFUN_NEW_GC(cmd_ref, NULL, TRUE));
    hash_put(self, "length", NFUN_NEW_GC(cmd_length, NULL, TRUE));
    hash_put(self, "export", NFUN_NEW_GC(cmd_export, NULL, TRUE));
    hash_put(self, "x", NFUN_NEW_GC(cmd_x , NULL, TRUE));
    hash_put(self, "stackframe", NFUN_NEW_GC(cmd_stackframe, NULL, TRUE));
    hash_put(self, "msleep", NFUN_NEW_GC(cmd_msleep, NULL, TRUE));
    hash_put(self, "raise", NFUN_NEW_GC(cmd_raise, NULL, TRUE));
    hash_put(self, "cd", NFUN_NEW_GC(cmd_cd, NULL, TRUE));
    hash_put(self, "popd", NFUN_NEW_GC(cmd_popd, NULL, TRUE));
    hash_put(self, "pushd", NFUN_NEW_GC(cmd_pushd, NULL, TRUE));
    hash_put(self, "block", NFUN_NEW_GC(cmd_block, NULL, TRUE));
    hash_put(self, "lc", NFUN_NEW_GC(cmd_lc, NULL, TRUE));
    hash_put(self, "uc", NFUN_NEW_GC(cmd_uc, NULL, TRUE));
    hash_put(self, "chomp", NFUN_NEW_GC(cmd_chomp, NULL, TRUE));
    hash_put(self, "chop", NFUN_NEW_GC(cmd_chop, NULL, TRUE));
    hash_put(self, "pomch", NFUN_NEW_GC(cmd_pomch, NULL, TRUE));
    hash_put(self, "funinfo", NFUN_NEW_GC(cmd_funinfo, NULL, TRUE));
    hash_put(self, "printf", NFUN_NEW_GC(cmd_printf, NULL, TRUE));
    hash_put(self, "join", NFUN_NEW_GC(cmd_join, NULL, TRUE));
    hash_put(self, "lines", NFUN_NEW_GC(cmd_lines, NULL, TRUE));
    hash_put(self, "rows", NFUN_NEW_GC(cmd_rows, NULL, TRUE));
    hash_put(self, "scan", NFUN_NEW_GC(cmd_scan, NULL, TRUE));
    hash_put(self, "split", NFUN_NEW_GC(cmd_split, NULL, TRUE));
    hash_put(self, "try", NFUN_NEW_GC(cmd_try, NULL, TRUE));
    hash_put(self, "errmsg", NFUN_NEW_GC(cmd_errmsg, NULL, TRUE));
    hash_put(self, "prompt", NFUN_NEW_GC(cmd_prompt, NULL, TRUE));

    hash_put(self, "-n", NFUN_NEW_GC(cmd_condition_n, NULL, TRUE));
    hash_put(self, "-z", NFUN_NEW_GC(cmd_condition_z, NULL, TRUE));
    hash_put(self, "-b", NFUN_NEW_GC(cmd_condition_b, NULL, TRUE));
    hash_put(self, "-c", NFUN_NEW_GC(cmd_condition_c, NULL, TRUE));
    hash_put(self, "-d", NFUN_NEW_GC(cmd_condition_d, NULL, TRUE));
    hash_put(self, "-f", NFUN_NEW_GC(cmd_condition_f, NULL, TRUE));
    hash_put(self, "-h", NFUN_NEW_GC(cmd_condition_h, NULL, TRUE));
    hash_put(self, "-L", NFUN_NEW_GC(cmd_condition_l, NULL, TRUE));
    hash_put(self, "-p", NFUN_NEW_GC(cmd_condition_p, NULL, TRUE));
    hash_put(self, "-t", NFUN_NEW_GC(cmd_condition_t, NULL, TRUE));
    hash_put(self, "-S", NFUN_NEW_GC(cmd_condition_s2, NULL, TRUE));
    hash_put(self, "-g", NFUN_NEW_GC(cmd_condition_g, NULL, TRUE));
    hash_put(self, "-k", NFUN_NEW_GC(cmd_condition_k, NULL, TRUE));
    hash_put(self, "-u", NFUN_NEW_GC(cmd_condition_u, NULL, TRUE));
    hash_put(self, "-r", NFUN_NEW_GC(cmd_condition_r, NULL, TRUE));
    hash_put(self, "-w", NFUN_NEW_GC(cmd_condition_w, NULL, TRUE));
    hash_put(self, "-x", NFUN_NEW_GC(cmd_condition_x, NULL, TRUE));
    hash_put(self, "-O", NFUN_NEW_GC(cmd_condition_o, NULL, TRUE));
    hash_put(self, "-G", NFUN_NEW_GC(cmd_condition_g2, NULL, TRUE));
    hash_put(self, "-e", NFUN_NEW_GC(cmd_condition_e, NULL, TRUE));
    hash_put(self, "-s", NFUN_NEW_GC(cmd_condition_s, NULL, TRUE));
    hash_put(self, "=", NFUN_NEW_GC(cmd_condition_eq, NULL, TRUE));
    hash_put(self, "!=", NFUN_NEW_GC(cmd_condition_neq, NULL, TRUE));
    hash_put(self, "-slt", NFUN_NEW_GC(cmd_condition_slt, NULL, TRUE));
    hash_put(self, "-sgt", NFUN_NEW_GC(cmd_condition_sgt, NULL, TRUE));
    hash_put(self, "-sle", NFUN_NEW_GC(cmd_condition_sle, NULL, TRUE));
    hash_put(self, "-sge", NFUN_NEW_GC(cmd_condition_sge, NULL, TRUE));
    hash_put(self, "-eq", NFUN_NEW_GC(cmd_condition_eq2, NULL, TRUE));
    hash_put(self, "-ne", NFUN_NEW_GC(cmd_condition_ne, NULL, TRUE));
    hash_put(self, "-lt", NFUN_NEW_GC(cmd_condition_lt, NULL, TRUE));
    hash_put(self, "-le", NFUN_NEW_GC(cmd_condition_le, NULL, TRUE));
    hash_put(self, "-gt", NFUN_NEW_GC(cmd_condition_gt, NULL, TRUE));
    hash_put(self, "-ge", NFUN_NEW_GC(cmd_condition_ge, NULL, TRUE));
    hash_put(self, "-nt", NFUN_NEW_GC(cmd_condition_nt, NULL, TRUE));
    hash_put(self, "-ot", NFUN_NEW_GC(cmd_condition_ot, NULL, TRUE));
    hash_put(self, "-ef", NFUN_NEW_GC(cmd_condition_ef, NULL, TRUE));
    hash_put(self, "=~", NFUN_NEW_GC(cmd_condition_re, NULL, TRUE));
    hash_put(self, "++", NFUN_NEW_GC(cmd_plusplus, NULL, TRUE));
    hash_put(self, "--", NFUN_NEW_GC(cmd_minusminus, NULL, TRUE));
    hash_put(self, "+", NFUN_NEW_GC(cmd_plus, NULL, TRUE));
    hash_put(self, "-", NFUN_NEW_GC(cmd_minus, NULL, TRUE));
    hash_put(self, "*", NFUN_NEW_GC(cmd_mult, NULL, TRUE));
    hash_put(self, "/", NFUN_NEW_GC(cmd_div, NULL, TRUE));
    hash_put(self, "mod", NFUN_NEW_GC(cmd_mod, NULL, TRUE));
    hash_put(self, "pow", NFUN_NEW_GC(cmd_pow, NULL, TRUE));
    hash_put(self, "abs", NFUN_NEW_GC(cmd_abs, NULL, TRUE));
    hash_put(self, "selector", NFUN_NEW_GC(cmd_selector, NULL, TRUE));
    hash_put(self, "sort", NFUN_NEW_GC(cmd_sort, NULL, TRUE));
    hash_put(self, "readline", NFUN_NEW_GC(cmd_readline, NULL, TRUE));
#if defined(HAVE_MIGEMO_H)
    hash_put(self, "migemo_match", NFUN_NEW_GC(cmd_migemo_match, NULL, TRUE));
#endif

    hash_put(self, "sub", NFUN_NEW_GC(cmd_sub, NULL, TRUE));
    hash_put(self, "time", NFUN_NEW_GC(cmd_time, NULL, TRUE));

    sObject* nfun = NFUN_NEW_GC(cmd_hash, NULL, TRUE);
    (void)nfun_put_option_with_argument(nfun, STRDUP("-key"));
    hash_put(self, "hash", nfun);
    nfun = NFUN_NEW_GC(cmd_ary, NULL, TRUE);
    (void)nfun_put_option_with_argument(nfun, STRDUP("-index"));
    (void)nfun_put_option_with_argument(nfun, STRDUP("-append"));
    hash_put(self, "ary", nfun);

    nfun = NFUN_NEW_GC(cmd_var, NULL, TRUE);
    (void)nfun_put_option_with_argument(nfun, STRDUP("-index"));
    hash_put(self, "var", nfun);

    nfun = NFUN_NEW_GC(cmd_add, NULL, TRUE);
    (void)nfun_put_option_with_argument(nfun, STRDUP("-number"));
    hash_put(self, "add", nfun);

    nfun = NFUN_NEW_GC(cmd_del, NULL, TRUE);
    (void)nfun_put_option_with_argument(nfun, STRDUP("-number"));
    hash_put(self, "del", nfun);

    nfun = NFUN_NEW_GC(cmd_index, NULL, TRUE);
    (void)nfun_put_option_with_argument(nfun, STRDUP("-number"));
    (void)nfun_put_option_with_argument(nfun, STRDUP("-count"));
    hash_put(self, "index", nfun);

    nfun = NFUN_NEW_GC(cmd_rindex, NULL, TRUE);
    (void)nfun_put_option_with_argument(nfun, STRDUP("-number"));
    (void)nfun_put_option_with_argument(nfun, STRDUP("-count"));
    hash_put(self, "rindex", nfun);

    nfun = NFUN_NEW_GC(cmd_each, NULL, TRUE);
    (void)nfun_put_option_with_argument(nfun, STRDUP("-number"));
    hash_put(self, "each", nfun);

    nfun = NFUN_NEW_GC(cmd_def, NULL, TRUE);
    (void)nfun_put_option_with_argument(nfun, STRDUP("-option-with-argument"));
    hash_put(self, "def", nfun);

    nfun = NFUN_NEW_GC(cmd_class, NULL, TRUE);
    (void)nfun_put_option_with_argument(nfun, STRDUP("-option-with-argument"));
    hash_put(self, "class", nfun);

    clear_matching_info_variable();
}

void uobject_init(sObject* self)
{
    hash_put(self, "run", NFUN_NEW_GC(cmd_mrun, NULL, FALSE));
    hash_put(self, "self", self);
    hash_put(self, "root", gRootObject);
    hash_put(self, "parent", SUOBJECT(self).mParent);
    hash_put(self, "show", NFUN_NEW_GC(cmd_mshow, NULL, FALSE));
}

void uobject_delete_gc(sObject* self)
{
   hash_it* it = SHASH(self).mEntryIt;

   while(it) {
      hash_it* next_it = it->mNextIt;
      hash_it_release(it);
      it = next_it;
   }
   
   FREE(SHASH(self).mTable);
   FREE(SUOBJECT(self).mName);
}

void uobject_delete_stack(sObject* self)
{
   hash_it* it = SHASH(self).mEntryIt;

   while(it) {
      hash_it* next_it = it->mNextIt;
      hash_it_release(it);
      it = next_it;
   }
   
   FREE(SHASH(self).mTable);
   FREE(SUOBJECT(self).mName);
}

int uobject_gc_children_mark(sObject* self)
{
    int count = 0;

    hash_it* it = SHASH(self).mEntryIt;
    while(it) {
        sObject* item = it->mItem;

        if((item->mFlg & GC_MARK) == 0) {
            item->mFlg |= GC_MARK;
            count++;

            count += object_gc_children_mark(item);
        }

        it = it->mNextIt;
    }

    return count;
}

sObject* nfun_new_from_gc(fNativeFun fun, sObject* parent, BOOL user_object)
{
   sObject* self = gc_get_free_object(T_NFUN, user_object);
   
   SNFUN(self).mNativeFun = fun;
   SNFUN(self).mParent = parent;

   memset(SNFUN(self).mOptions,0, sizeof(option_hash_it)*PIPES_OPTION_MAX);

   return self;
}

void nfun_delete_gc(sObject* self)
{
    int i;
    for(i=0; i<PIPES_OPTION_MAX; i++) {
        if(SNFUN(self).mOptions[i].mKey) { FREE(SNFUN(self).mOptions[i].mKey); }
        if(SNFUN(self).mOptions[i].mArg) { FREE(SNFUN(self).mOptions[i].mArg); }
    }
}

static int options_hash_fun(char* key)
{
    int value = 0;
    while(*key) {
        value += *key;
        key++;
    }
    return value % PIPES_OPTION_MAX;
}

BOOL nfun_put_option_with_argument(sObject* self, MANAGED char* key)
{
    int hash_value = options_hash_fun(key);

    option_hash_it* p = SNFUN(self).mOptions + hash_value;
    while(1) {
        if(p->mKey) {
            p++;
            if(p == SNFUN(self).mOptions + hash_value) {
                return FALSE;
            }
            else if(p == SNFUN(self).mOptions + PIPES_OPTION_MAX) {
                p = SNFUN(self).mOptions;
            }
        }
        else {
            p->mKey = MANAGED key;
            return TRUE;
        }
    }
}

BOOL nfun_option_with_argument_item(sObject* self, char* key)
{
    int hash_value = options_hash_fun(key);
    option_hash_it* p = SNFUN(self).mOptions + hash_value;

    while(1) {
        if(p->mKey) {
            if(strcmp(p->mKey, key) == 0) {
                return TRUE;
            }
            else {
                p++;
                if(p == SNFUN(self).mOptions + hash_value) {
                    return FALSE;
                }
                else if(p == SNFUN(self).mOptions + PIPES_OPTION_MAX) {
                    p = SNFUN(self).mOptions;
                }
            }
        }
        else {
            return FALSE;
        }
    }
}

int nfun_gc_children_mark(sObject* self)
{
    int count = 0;

    sObject* parent = SNFUN(self).mParent;
    if(parent) {
        if((parent->mFlg & GC_MARK) == 0) {
            parent->mFlg |= GC_MARK;
            count++;
            count += object_gc_children_mark(parent);
        }
    }

    return count;
}

sObject* access_object(char* name, sObject** current, sObject* running_object)
{
    sObject* object;
    if(running_object) {
        object = hash_item(SFUN(running_object).mLocalObjects, name);
        if(object) { return object; }
    }

    while(1) {
        object = hash_item(*current, name);

        if(object || *current == gRootObject) { return object; }

        *current = SUOBJECT((*current)).mParent;
    }
}

sObject* access_object2(char* name, sObject* current, sObject* running_object)
{
    sObject* object = hash_item(SFUN(running_object).mLocalObjects, name);
    if(object) return object;

    return hash_item(current, name);
}

sObject* access_object3(char* name, sObject** current)
{
    sObject* object;

    while(1) {
        object = hash_item(*current, name);

        if(object || *current == gRootObject) { return object; }

        *current = SUOBJECT((*current)).mParent;
    }
}

sObject* external_new_from_gc(char* path, BOOL user_object)
{
   sObject* self = gc_get_free_object(T_EXTERNAL, user_object);
   
   strncpy(SEXTERNAL(self).mPath, path, PATH_MAX);

   return self;
}

sObject* extobj_new_from_gc(void* object, fExtObjMarkFun mark_fun, fExtObjFreeFun free_fun, BOOL user_object)
{
   sObject* self = gc_get_free_object(T_EXTOBJ, user_object);
   
   SEXTOBJ(self).mObject = object;
   SEXTOBJ(self).mMarkFun = mark_fun;
   SEXTOBJ(self).mFreeFun = free_fun;

   return self;
}

